
import random
from .const_args import Dist
import math

class Node:

    def __init__(self, name):
        self.name = name
        self.status = "Dormant"
        # Active, Dormant, Failed, Maintenance NAM=Not Allowed for Maintenance, Warm, CycOn, CycDown, Minor
        self.AM = True  # 该task下是否允许维修
        self.failure_time = 0   # 在对应dist下的寿命
        self.failure_time_m = 0  # 离散型分布不影响损耗，引入一个参数记录此前的寿命
        self.failure_rate = 1   # 寿命的消耗速率，因温备份引入
        self.repair_time = 0
        self.children = []
        self.parent = None
        self.last_failure_dist = None  # 上一个失效分布
        self.failure_dist = None
        self.repair_dist = None
        self.node_type = None
        self.kvalue = 1
        self.is_basic = False    # 默认为True在获取节点的函数中会被更新
        self.is_used = False  # 该节点在该任务中被使用/闲置
        self.is_restable = False  # restless表示基节点即使不在任务中被使用，也会继续消耗寿命
        self.prob = 0  # 是累积损伤模型的概率值, 仅在初始化和修复时更新
        self.change_in_event = False  # 避免重复更新节点设置的变量
        self.is_cyc = False
        self.cyc = [0, 0, 0]  # 工作时间、工作间休息时间、当前阶段剩余时间
        self.is_sys = False


    def get_prob(self):  # 仅在维修后获取
        self.prob = random.random()
        return self.prob
    '''class SoF(Distribution):

    def __init__(self, disttype, args):
        super(SoF, self).__init__(disttype, args)
        self.p = args[0]
        self.n = args[1]#使用次数
        self.m = args[2]#允许失败的次数

    def cdf(self):
        pass

    def inverse_cdf(self, probability):
        pass'''
    def get_failure_time_sof(self, duration):
        time_piece =  duration/(self.failure_dist.n+1)
        sucess_times = 0
        fail_times = 0
        failed = False ##是否超过m次失败
        while sucess_times + fail_times <self.failure_dist.n:
            if self.repair_time < time_piece:
                self.repair_time = 0
                if random.random() < self.failure_dist.p:
                    sucess_times += 1
                else:
                    fail_times += 1
                    if fail_times >= self.failure_dist.m and not failed:
                        failed = True
                        k = sucess_times + fail_times
                    if self.repair_dist:
                        self.repair_time = self.get_repair_time() #获取维修时间
                    else:
                        self.repair_time = math.inf
            else:   
                self.repair_time -= time_piece
                fail_times += 1
                if fail_times >= self.failure_dist.m and not failed:
                    failed = True
                    k = sucess_times + fail_times
        self.failure_time = math.inf if not failed else k*time_piece
        self.repair_time = max(0, self.repair_time-time_piece)
        return self.failure_time, self.repair_time  #返回的维修时间是下阶段的剩余维修时间

    def get_failure_time_b(self, duration):
        if self.failure_dist.disttype == Dist.bernoulli:
            prop = random.random()
            if prop > self.failure_dist.p:
                self.failure_time = random.uniform(0,duration)
                return self.failure_time
            else:
                self.failure_time = math.inf
                return self.failure_time
                            
        elif self.failure_dist.disttype == Dist.binomial:
            prop = random.random()
            if prop < self.failure_dist.cdf():
                self.failure_time = random.uniform(0,duration)
                return self.failure_time
            else:
                self.failure_time = math.inf
                return self.failure_time
        else:
            raise ValueError(f"不合理的分布类型{self.failure_dist.disttype}")

    def get_failure_time(self):
        if self.last_failure_dist is None:
            self.failure_time = self.failure_dist.inverse_cdf(self.prob)
        else:
            # 在上个分布下的总寿命 - 在上个分布下的剩余寿命 = 在上个分布下的等效存活时间
            last_equivalent_survival_time = self.last_failure_dist.inverse_cdf(self.prob) - self.failure_time
            # 在现在分布下的等效存活时间
            equivalent_survival_time = self.failure_dist.inverse_cdf(self.last_failure_dist.cdf(last_equivalent_survival_time))
            # 在现在分布下的总时间 - 在现在分布下的等效存活时间 = 在现在分布下的剩余寿命
            self.failure_time = self.failure_dist.inverse_cdf(self.prob) - equivalent_survival_time
        return self.failure_time

    def get_repair_time(self):
        """
            获取模型的维修时长
            根据五种不同的分布类型返回相应的维修时长

            Args：
            self：节点信息

            Return：
            维修时长

        """
        # None 类型的修复会得到-1的时间
        prob = random.random()
        self.repair_time = self.repair_dist.inverse_cdf(prob)

        return self.repair_time

    def update_status(self):
        if self.is_basic:
            return self.status

        # 递归获得子节点状态
        for child in self.children:
            child.update_status()

        # 根据节点类型确定状态
        if self.node_type == "parallel":  # 目前假设standby的子节点不出现在其他逻辑之下
            if all(child.status in ["Failed", "Maintenance", "Dormant", "Warm","Maintenance_SOF"] for child in self.children):
                self.status = "Failed"
            else:
                self.status = "Active"
            if all(child.status in ["Dormant", "Warm"] for child in self.children):
                self.status = "Dormant"
        
        elif self.node_type == "standby" or self.node_type == "warm_standby":  # 目前假设standby的子节点不出现在其他逻辑之下
            active_count = sum(child.status in ["Active", "CycOn", "CycDown"] for child in self.children)
            self.status = "Active" if active_count >= self.kvalue else "Failed"
            #if all(child.status in ["Dormant", "Warm"] for child in self.children):
                #self.status = "Dormant"

        elif self.node_type == "series":
            self.status = "Active" if all(child.status in ["Active", "CycOn", "CycDown"] for child in self.children) else "Failed"

            if all(child.status in ["Dormant", "Warm"] for child in self.children):
                self.status = "Dormant"

        elif self.node_type == "vote":
            active_count = sum(child.status in ["Active", "CycOn", "CycDown"] for child in self.children)
            self.status = "Active" if active_count >= self.kvalue else "Failed"

            if all(child.status in ["Dormant", "Warm"] for child in self.children):
                self.status = "Dormant"

        elif self.node_type == "bridge":  # 认为 bridge 只能有 5 个子节点
            if self.children[4].status in ["Active", "CycOn"]:  # E节点正常，则视为并联的串联
                # AC同时失效 or BD同时失效 输出 failed
                if (self.children[0].status in ["Failed", "Maintenance", "Dormant", "Warm"]
                        and self.children[2].status in ["Failed", "Maintenance", "Dormant", "Warm","Maintenance_SOF"]
                        or self.children[1].status in ["Failed", "Maintenance", "Dormant", "Warm","Maintenance_SOF"]
                        and self.children[3].status in ["Failed", "Maintenance", "Dormant", "Warm","Maintenance_SOF"]):
                    self.status = "Failed"
                else:
                    self.status = "Active"

            else:  # E节点失效，则视为串联的并联
                if (self.children[0].status in ["Active", "CycOn", "CycDown"]
                        and self.children[1].status in ["Active", "CycOn", "CycDown"]
                        or self.children[2].status in ["Active", "CycOn", "CycDown"]
                        and self.children[3].status in ["Active", "CycOn", "CycDown"]):
                    self.status = "Active"
                else:
                    self.status = "Failed"

            if all(child.status in ["Dormant", "Warm"] for child in self.children):
                self.status = "Dormant"

        elif self.node_type is None:
            # 对于model1这样的节点，其没类型，考虑首个子节点“总体”
            if self.children:
                self.status = self.children[0].status
        else:
            raise ValueError(f"未知逻辑类型: {self.node_type}")

        return self.status

    def find_basic(self):
        # Collect all basic nodes in a list
        basic_nodes = []
        self._find_basic_helper(basic_nodes)
        return basic_nodes

    def _find_basic_helper(self, basic_nodes):
        if self.is_basic:
            basic_nodes.append(self)
        else:
            for child in self.children:
                child._find_basic_helper(basic_nodes)

    def __str__(self):
        return f"Node: {self.name}\n" \
               f"  Status: {self.status}\n" \
               f"  Failure Time: {self.failure_time}\n" \
               f"  Repair Time: {self.repair_time}\n" \
               f"  Children: {self.children}\n" \
               f"  Parent: {self.parent}\n" \
               f"  Failure Distribution: {self.failure_dist}\n" \
               f"  Repair Distribution: {self.repair_dist}\n" \
               f"  Node Type: {self.node_type}\n" \
               f"  K Value: {self.kvalue}\n" \
               f"  Is Basic: {self.is_basic}"